package logic;

public class MoveValidator {

	private ChessGame chessGame;
	private Piece sourcePiece;
	private Piece targetPiece;

	public MoveValidator(ChessGame chessGame) {
		this.chessGame = chessGame;
	}

	/**
	 * checks if the specified move is valid
	 * 
	 * @param move
	 *            to validate
	 * @return true if move is valid, false if move is invalid
	 */
	public boolean isMoveValid(Move move, boolean debug) {
		int sourceRow = move.getSourceRow();
		int sourceColumn = move.getSourceColumn();
		int targetRow = move.getTargetRow();
		int targetColumn = move.getTargetColumn();

		sourcePiece = chessGame.getNonCapturedPieceAtLocation(sourceRow,
				sourceColumn);
		targetPiece = chessGame.getNonCapturedPieceAtLocation(targetRow,
				targetColumn);
		// System.out.println("source piece: "+sourcePiece);
		// System.out.println("target: "+targetPiece);

		// source piece does not exist
		if (sourcePiece == null) {
			if (debug)
				System.out.println("no source piece");
			return false;
		}

		// source piece has right color?
		if (sourcePiece.getColor() == Piece.COLOR_RED
				&& this.chessGame.getGameState() == ChessGame.GAME_STATE_RED) {
			// true;
		} else if (sourcePiece.getColor() == Piece.COLOR_GREEN
				&& this.chessGame.getGameState() == ChessGame.GAME_STATE_GREEN) {
			// true;
		} else {
			// System.out.println("it's not your turn");
			return false;
		}

		// check if target location within boundaries
		if (targetRow < Piece.ROW_1 || targetRow > Piece.ROW_10
				|| targetColumn < Piece.COLUMN_A
				|| targetColumn > Piece.COLUMN_I) {
			if (debug)
				System.out.println("target row or column out of scope");
			return false;
		}

		// validate piecee movement rules
		boolean validPieceMove = false;
		switch (sourcePiece.getType()) {
		case Piece.TYPE_CANNON:
			validPieceMove = isValidCannonMove(sourceRow, sourceColumn,
					targetRow, targetColumn);
			break;
		case Piece.TYPE_ELEPHANT:
			validPieceMove = isValidElephantMove(sourceRow, sourceColumn,
					targetRow, targetColumn);
			break;
		case Piece.TYPE_GUARD:
			validPieceMove = isValidGuardMove(sourceRow, sourceColumn,
					targetRow, targetColumn);
			break;
		case Piece.TYPE_KING:
			validPieceMove = isValidKingMove(sourceRow, sourceColumn,
					targetRow, targetColumn);
			break;
		case Piece.TYPE_HOURSE:
			validPieceMove = isValidKnightMove(sourceRow, sourceColumn,
					targetRow, targetColumn);
			break;
		case Piece.TYPE_PAWN:
			validPieceMove = isValidPawnMove(sourceRow, sourceColumn,
					targetRow, targetColumn, sourcePiece);
			break;
		case Piece.TYPE_ROOK:
			validPieceMove = isValidRookMove(sourceRow, sourceColumn,
					targetRow, targetColumn);
			break;
		default:
			break;
		}

		if (!validPieceMove) {
			return false;
		} else {

		}
		// System.out.println(validPieceMove);
		return true;
	}

	private boolean isTargetLocationCaptureable() {
		if (targetPiece == null)
			return false;
		else if (targetPiece.getColor() != sourcePiece.getColor())
			return true;
		else
			return false;
	}

	private boolean isTargetLocationFree() {
		return targetPiece == null;
	}

	private boolean isValidRookMove(int sourceRow, int sourceColumn,
			int targetRow, int targetColumn) {
		if (isTargetLocationFree() || isTargetLocationCaptureable()) {
			// ok
			// System.out.println("rook location free");
		} else {
			// System.out.println("target location not free and not captureable");
			return false;
		}

		boolean isValid = false;

		if (sourceColumn == targetColumn && sourceRow < targetRow) {
			isValid = !arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, 1, 0);
		} else if (sourceColumn == targetColumn && sourceRow > targetRow) {
			isValid = !arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, -1, 0);
		} else if (sourceRow == targetRow && sourceColumn < targetColumn) {
			isValid = !arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, 0, 1);
		} else if (sourceRow == targetRow && sourceColumn > targetColumn) {
			isValid = !arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, 0, -1);
		} else {
			// System.out.println("not moving straight");
			isValid = false;
		}

		return isValid;
	}

	private boolean arePiecesBetweenSourceAndTarget(int sourceRow,
			int sourceColumn, int targetRow, int targetColumn,
			int rowIncrementPerStep, int columnIncrementPerStep) {

		int currentRow = sourceRow + rowIncrementPerStep;
		int currentColumn = sourceColumn + columnIncrementPerStep;
		while (true) {
			if (currentRow == targetRow && currentColumn == targetColumn) {
				break;
			}
			if (currentRow < Piece.ROW_1 || currentRow > Piece.ROW_10
					|| currentColumn < Piece.COLUMN_A
					|| currentColumn > Piece.COLUMN_I) {
				break;
			}

			if (this.chessGame.isNonCapturedPieceAtLocation(currentRow,
					currentColumn)) {
				// System.out.println("pieces in between source and target");
				return true;
			}

			currentRow += rowIncrementPerStep;
			currentColumn += columnIncrementPerStep;
		}
		return false;
	}

	private int countPiecesBetweenSourceAndTarget(int sourceRow,
			int sourceColumn, int targetRow, int targetColumn,
			int rowIncrementPerStep, int columnIncrementPerStep) {

		int currentRow = sourceRow + rowIncrementPerStep;
		int currentColumn = sourceColumn + columnIncrementPerStep;
		int count = 0;
		while (true) {
			if (currentRow == targetRow && currentColumn == targetColumn) {
				break;
			}
			if (currentRow < Piece.ROW_1 || currentRow > Piece.ROW_10
					|| currentColumn < Piece.COLUMN_A
					|| currentColumn > Piece.COLUMN_I) {
				break;
			}

			if (this.chessGame.isNonCapturedPieceAtLocation(currentRow,
					currentColumn)) {
				// System.out.println("pieces in between source and target");
				count++;
			}

			currentRow += rowIncrementPerStep;
			currentColumn += columnIncrementPerStep;
		}
		return count;
	}

	private boolean isValidKnightMove(int sourceRow, int sourceColumn,
			int targetRow, int targetColumn) {
		if (isTargetLocationFree() || isTargetLocationCaptureable()) {
			// true
		} else {
			// System.out.println("target location not free and not captureable");
			return false;
		}

		if (sourceRow + 2 == targetRow
				&& sourceColumn + 1 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow + 1,
						sourceColumn)) {
			return true;
		} else if (sourceRow + 1 == targetRow
				&& sourceColumn + 2 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow,
						sourceColumn + 1)) {
			return true;
		} else if (sourceRow - 1 == targetRow
				&& sourceColumn + 2 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow,
						sourceColumn + 1)) {
			return true;
		} else if (sourceRow - 2 == targetRow
				&& sourceColumn + 1 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow - 1,
						sourceColumn)) {
			return true;
		} else if (sourceRow - 2 == targetRow
				&& sourceColumn - 1 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow - 1,
						sourceColumn)) {
			return true;
		} else if (sourceRow - 1 == targetRow
				&& sourceColumn - 2 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow,
						sourceColumn - 1)) {
			return true;
		} else if (sourceRow + 1 == targetRow
				&& sourceColumn - 2 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow,
						sourceColumn - 1)) {
			return true;
		} else if (sourceRow + 2 == targetRow
				&& sourceColumn - 1 == targetColumn
				&& !this.chessGame.isNonCapturedPieceAtLocation(sourceRow + 1,
						sourceColumn)) {
			return true;
		} else {
			return false;
		}
	}

	private boolean isValidElephantMove(int sourceRow, int sourceColumn,
			int targetRow, int targetColumn) {
		if (isTargetLocationFree() || isTargetLocationCaptureable()) {
			// true
		} else {
			// System.out.println("target location not free and not captureable");
			return false;
		}
		boolean isValid = false;
		int gameState = chessGame.getGameState();
		if (gameState == ChessGame.GAME_STATE_GREEN) {
			if (targetRow > Piece.ROW_5) {
				isValid = false;
			} else if (sourceColumn + 2 == targetColumn
					&& sourceRow + 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, 1, 1);
			} else if (sourceColumn + 2 == targetColumn
					&& sourceRow - 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, -1, 1);
			} else if (sourceColumn - 2 == targetColumn
					&& sourceRow - 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, -1, -1);
			} else if (sourceColumn - 2 == targetColumn
					&& sourceRow + 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, 1, -1);
			} else {
				isValid = false;
			}
		} else if (gameState == ChessGame.GAME_STATE_RED) {
			if (targetRow < Piece.ROW_6) {
				isValid = false;
			} else if (sourceColumn + 2 == targetColumn
					&& sourceRow + 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, 1, 1);
			} else if (sourceColumn + 2 == targetColumn
					&& sourceRow - 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, -1, 1);
			} else if (sourceColumn - 2 == targetColumn
					&& sourceRow - 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, -1, -1);
			} else if (sourceColumn - 2 == targetColumn
					&& sourceRow + 2 == targetRow) {
				isValid = !arePiecesBetweenSourceAndTarget(sourceRow,
						sourceColumn, targetRow, targetColumn, 1, 0);
			} else {
				isValid = false;
			}
		} else {
			// System.out.println("unknows game state");
			isValid = false;
		}
		return isValid;
	}

	private boolean isValidGuardMove(int sourceRow, int sourceColumn,
			int targetRow, int targetColumn) {
		if (isTargetLocationFree() || isTargetLocationCaptureable()) {
			// true
		} else {
			// System.out.println("target location not free and not captureable");
			return false;
		}
		int gameState = chessGame.getGameState();
		if (targetColumn > Piece.COLUMN_F || targetColumn < Piece.COLUMN_D) {
			return false;
		}

		if (gameState == ChessGame.GAME_STATE_GREEN) {
			if (targetRow > Piece.ROW_3 || targetRow < Piece.ROW_1) {
				return false;
			} else {
				if (sourceRow + 1 == targetRow
						&& sourceColumn + 1 == targetColumn) {
					return true;
				} else if (sourceRow - 1 == targetRow
						&& sourceColumn + 1 == targetColumn) {
					return true;
				} else if (sourceRow - 1 == targetRow
						&& sourceColumn - 1 == targetColumn) {
					return true;
				} else if (sourceRow + 1 == targetRow
						&& sourceColumn - 1 == targetColumn) {
					return true;
				} else {
					return false;
				}

			}
		} else if (gameState == ChessGame.GAME_STATE_RED) {
			if (targetRow > Piece.ROW_10 || targetRow < Piece.ROW_8) {
				return false;
			} else {
				if (sourceRow + 1 == targetRow
						&& sourceColumn + 1 == targetColumn) {
					return true;
				} else if (sourceRow - 1 == targetRow
						&& sourceColumn + 1 == targetColumn) {
					return true;
				} else if (sourceRow - 1 == targetRow
						&& sourceColumn - 1 == targetColumn) {
					return true;
				} else if (sourceRow + 1 == targetRow
						&& sourceColumn - 1 == targetColumn) {
					return true;
				} else {
					return false;
				}
			}
		} else
			return false;
	}

	private boolean isValidKingMove(int sourceRow, int sourceColumn,
			int targetRow, int targetColumn) {
		if (isTargetLocationFree() || isTargetLocationCaptureable()) {
			// true
		} else {
			// System.out.println("target location not free and not captureable");
			return false;
		}
		int gameState = chessGame.getGameState();
		if (targetColumn > Piece.COLUMN_F || targetColumn < Piece.COLUMN_D) {
			return false;
		}

		if (gameState == ChessGame.GAME_STATE_GREEN) {
			if (targetRow > Piece.ROW_3 || targetRow < Piece.ROW_1) {
				return false;
			} else {
				if (sourceRow + 1 == targetRow && sourceColumn == targetColumn) {
					return true;

				} else if (sourceRow == targetRow
						&& sourceColumn + 1 == targetColumn) {
					return true;
				} else if (sourceRow - 1 == targetRow
						&& sourceColumn == targetColumn) {
					return true;
				} else if (sourceRow == targetRow
						&& sourceColumn - 1 == targetColumn) {
					return true;
				} else {
					return false;
				}

			}
		} else if (gameState == ChessGame.GAME_STATE_RED) {
			if (targetRow > Piece.ROW_10 || targetRow < Piece.ROW_8) {
				return false;
			} else {
				if (sourceRow + 1 == targetRow && sourceColumn == targetColumn) {
					return true;
				} else if (sourceRow == targetRow
						&& sourceColumn + 1 == targetColumn) {
					return true;
				} else if (sourceRow - 1 == targetRow
						&& sourceColumn == targetColumn) {
					return true;
				} else if (sourceRow == targetRow
						&& sourceColumn - 1 == targetColumn) {
					return true;
				} else {
					return false;
				}
			}
		} else
			return false;
	}

	private boolean isValidCannonMove(int sourceRow, int sourceColumn,
			int targetRow, int targetColumn) {
		if (isTargetLocationFree() || isTargetLocationCaptureable()) {
			// ok
		} else {
			// System.out.println("target location not free and not captureable");
			 return false;
		}

		boolean isValid = false;
		int color = chessGame.getNonCapturedPieceAtLocation(sourceRow,
				sourceColumn).getColor() == Piece.COLOR_GREEN ? Piece.COLOR_RED
				: Piece.COLOR_GREEN;
		boolean isNonCapturedPiece = chessGame.isNonCapturedPieceAtLocation(
				color, targetRow, targetColumn);
		if (sourceColumn == targetColumn && sourceRow < targetRow) {
			// System.out.println(countPiecesBetweenSourceAndTarget(sourceRow,
			// sourceColumn, targetRow, targetColumn, 1, 0));
			return (countPiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, 1, 0) == 1
					&& arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
							targetRow, targetColumn, 1, 0) && isNonCapturedPiece)
					|| (!arePiecesBetweenSourceAndTarget(sourceRow,
							sourceColumn, targetRow, targetColumn, 1, 0) && !isNonCapturedPiece);

		} else if (sourceColumn == targetColumn && sourceRow > targetRow) {
			// System.out.println(countPiecesBetweenSourceAndTarget(sourceRow,
			// sourceColumn, targetRow, targetColumn, -1, 0));
			return (countPiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, -1, 0) == 1
					&& arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
							targetRow, targetColumn, -1, 0) && isNonCapturedPiece)
					|| (!arePiecesBetweenSourceAndTarget(sourceRow,
							sourceColumn, targetRow, targetColumn, -1, 0) && !isNonCapturedPiece);
		} else if (sourceRow == targetRow && sourceColumn < targetColumn) {
			// System.out.println(countPiecesBetweenSourceAndTarget(sourceRow,
			// sourceColumn, targetRow, targetColumn, 0, 1));
			return (countPiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, 0, 1) == 1
					&& arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
							targetRow, targetColumn, 0, 1) && isNonCapturedPiece)
					|| (!arePiecesBetweenSourceAndTarget(sourceRow,
							sourceColumn, targetRow, targetColumn, 0, 1) && !isNonCapturedPiece);
		} else if (sourceRow == targetRow && sourceColumn > targetColumn) {
			// System.out.println(countPiecesBetweenSourceAndTarget(sourceRow,
			// sourceColumn, targetRow, targetColumn, 0, -1));
			return (countPiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
					targetRow, targetColumn, 0, -1) == 1
					&& arePiecesBetweenSourceAndTarget(sourceRow, sourceColumn,
							targetRow, targetColumn, 0, -1) && isNonCapturedPiece)
					|| (!arePiecesBetweenSourceAndTarget(sourceRow,
							sourceColumn, targetRow, targetColumn, 0, -1) && !isNonCapturedPiece);
		} else {
			// System.out.println("not moving straight");
			isValid = false;
		}

		return isValid;
	}

	private boolean isValidPawnMove(int sourceRow, int sourceColumn,
			int targetRow, int targetColumn, Piece sourcePiece) {
		if (isTargetLocationFree() || isTargetLocationCaptureable()) {
			// true
		} else {
			// System.out.println("target location not free and not captureable");
			return false;
		}
		int gameState = chessGame.getGameState();
		boolean passRiver = sourcePiece.passRiver();
		if (!passRiver) {
			if (gameState == ChessGame.GAME_STATE_GREEN
					&& sourceColumn == targetColumn
					&& sourceRow + 1 == targetRow) {
				return true;
			} else if (gameState == ChessGame.GAME_STATE_RED
					&& sourceColumn == targetColumn
					&& sourceRow - 1 == targetRow) {
				return true;
			} else {
				return false;
			}
		} else if (passRiver) {
			if (sourceRow + 1 == targetRow && sourceColumn == targetColumn) {
				return gameState == ChessGame.GAME_STATE_GREEN;
			} else if (sourceRow - 1 == targetRow
					&& sourceColumn == targetColumn) {
				return gameState == ChessGame.GAME_STATE_RED;
			} else if (sourceRow == targetRow
					&& sourceColumn - 1 == targetColumn) {
				return true;
			} else if (sourceRow == targetRow
					&& sourceColumn + 1 == targetColumn) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}

	}

	public static void main(String[] args) {
		ChessGame ch = new ChessGame();
		MoveValidator mo = new MoveValidator(ch);
		Move move = null;
		boolean isValid;

		int sourceRow;
		int sourceColumn;
		int targetRow;
		int targetColumn;
		int testCounter = 1;

		// ok
		// 1
		System.out.println(ch.showGameState());
		sourceRow = Piece.ROW_3;
		sourceColumn = Piece.COLUMN_B;
		targetRow = Piece.ROW_2;
		targetColumn = Piece.COLUMN_I;
		move = new Move(sourceRow, sourceColumn, targetRow, targetColumn);
		isValid = mo.isMoveValid(move, false);
		ch.movePiece(move);
		System.out.println(testCounter + ". test result: " + isValid);
		testCounter++;

		// 2
		System.out.println(ch.showGameState());
		sourceRow = Piece.ROW_7;
		sourceColumn = Piece.COLUMN_I;
		targetRow = Piece.ROW_6;
		targetColumn = Piece.COLUMN_I;
		move = new Move(sourceRow, sourceColumn, targetRow, targetColumn);
		isValid = mo.isMoveValid(move, false);
		ch.movePiece(move);
		System.out.println(testCounter + ". test result: " + isValid);
		testCounter++;

		// 3
		System.out.println(ch.showGameState());
		sourceRow = Piece.ROW_1;
		sourceColumn = Piece.COLUMN_E;
		targetRow = Piece.ROW_2;
		targetColumn = Piece.COLUMN_E;
		move = new Move(sourceRow, sourceColumn, targetRow, targetColumn);
		isValid = mo.isMoveValid(move, false);
		ch.movePiece(move);
		System.out.println(testCounter + ". test result: " + isValid);
		testCounter++;

	}
}
